fix(llmobs): openai-java payload mapping for responses, tool metadata, and prompt tracking#10644
fix(llmobs): openai-java payload mapping for responses, tool metadata, and prompt tracking#10644
Conversation
BenchmarksStartupParameters
See matching parameters
SummaryFound 0 performance improvements and 0 performance regressions! Performance is the same for 63 metrics, 8 unstable metrics. Startup time reports for insecure-bankgantt
title insecure-bank - global startup overhead: candidate=1.60.0-SNAPSHOT~9911c514e7, baseline=1.61.0-SNAPSHOT~5580c61ac4
dateFormat X
axisFormat %s
section tracing
Agent [baseline] (1.057 s) : 0, 1057214
Total [baseline] (8.82 s) : 0, 8819984
Agent [candidate] (1.055 s) : 0, 1055114
Total [candidate] (8.825 s) : 0, 8825318
section iast
Agent [baseline] (1.235 s) : 0, 1234646
Total [baseline] (9.572 s) : 0, 9572364
Agent [candidate] (1.223 s) : 0, 1222502
Total [candidate] (9.513 s) : 0, 9512836
gantt
title insecure-bank - break down per module: candidate=1.60.0-SNAPSHOT~9911c514e7, baseline=1.61.0-SNAPSHOT~5580c61ac4
dateFormat X
axisFormat %s
section tracing
crashtracking [baseline] (1.201 ms) : 0, 1201
crashtracking [candidate] (1.186 ms) : 0, 1186
BytebuddyAgent [baseline] (629.845 ms) : 0, 629845
BytebuddyAgent [candidate] (627.923 ms) : 0, 627923
AgentMeter [baseline] (29.345 ms) : 0, 29345
AgentMeter [candidate] (29.337 ms) : 0, 29337
GlobalTracer [baseline] (255.926 ms) : 0, 255926
GlobalTracer [candidate] (256.145 ms) : 0, 256145
AppSec [baseline] (31.594 ms) : 0, 31594
AppSec [candidate] (31.68 ms) : 0, 31680
Debugger [baseline] (59.566 ms) : 0, 59566
Debugger [candidate] (59.394 ms) : 0, 59394
Remote Config [baseline] (582.515 µs) : 0, 583
Remote Config [candidate] (577.755 µs) : 0, 578
Telemetry [baseline] (8.051 ms) : 0, 8051
Telemetry [candidate] (7.984 ms) : 0, 7984
Flare Poller [baseline] (5.031 ms) : 0, 5031
Flare Poller [candidate] (4.941 ms) : 0, 4941
section iast
crashtracking [baseline] (1.199 ms) : 0, 1199
crashtracking [candidate] (1.186 ms) : 0, 1186
BytebuddyAgent [baseline] (801.59 ms) : 0, 801590
BytebuddyAgent [candidate] (793.791 ms) : 0, 793791
AgentMeter [baseline] (11.592 ms) : 0, 11592
AgentMeter [candidate] (11.341 ms) : 0, 11341
GlobalTracer [baseline] (248.392 ms) : 0, 248392
GlobalTracer [candidate] (246.235 ms) : 0, 246235
IAST [baseline] (25.584 ms) : 0, 25584
IAST [candidate] (25.348 ms) : 0, 25348
AppSec [baseline] (26.757 ms) : 0, 26757
AppSec [candidate] (26.361 ms) : 0, 26361
Debugger [baseline] (68.89 ms) : 0, 68890
Debugger [candidate] (68.68 ms) : 0, 68680
Remote Config [baseline] (535.563 µs) : 0, 536
Remote Config [candidate] (525.583 µs) : 0, 526
Telemetry [baseline] (10.237 ms) : 0, 10237
Telemetry [candidate] (9.616 ms) : 0, 9616
Flare Poller [baseline] (3.722 ms) : 0, 3722
Flare Poller [candidate] (3.463 ms) : 0, 3463
Startup time reports for petclinicgantt
title petclinic - global startup overhead: candidate=1.60.0-SNAPSHOT~9911c514e7, baseline=1.61.0-SNAPSHOT~5580c61ac4
dateFormat X
axisFormat %s
section tracing
Agent [baseline] (1.062 s) : 0, 1062086
Total [baseline] (10.935 s) : 0, 10934666
Agent [candidate] (1.056 s) : 0, 1056021
Total [candidate] (11.036 s) : 0, 11036221
section appsec
Agent [baseline] (1.244 s) : 0, 1244384
Total [baseline] (11.223 s) : 0, 11222893
Agent [candidate] (1.243 s) : 0, 1243294
Total [candidate] (11.251 s) : 0, 11251193
section iast
Agent [baseline] (1.227 s) : 0, 1227499
Total [baseline] (11.381 s) : 0, 11380525
Agent [candidate] (1.228 s) : 0, 1227712
Total [candidate] (11.237 s) : 0, 11236774
section profiling
Agent [baseline] (1.182 s) : 0, 1182316
Total [baseline] (11.027 s) : 0, 11026737
Agent [candidate] (1.179 s) : 0, 1179376
Total [candidate] (11.051 s) : 0, 11050740
gantt
title petclinic - break down per module: candidate=1.60.0-SNAPSHOT~9911c514e7, baseline=1.61.0-SNAPSHOT~5580c61ac4
dateFormat X
axisFormat %s
section tracing
crashtracking [baseline] (1.199 ms) : 0, 1199
crashtracking [candidate] (1.191 ms) : 0, 1191
BytebuddyAgent [baseline] (631.089 ms) : 0, 631089
BytebuddyAgent [candidate] (629.385 ms) : 0, 629385
AgentMeter [baseline] (29.417 ms) : 0, 29417
AgentMeter [candidate] (29.236 ms) : 0, 29236
GlobalTracer [baseline] (257.28 ms) : 0, 257280
GlobalTracer [candidate] (255.533 ms) : 0, 255533
AppSec [baseline] (31.907 ms) : 0, 31907
AppSec [candidate] (31.686 ms) : 0, 31686
Debugger [baseline] (60.684 ms) : 0, 60684
Debugger [candidate] (60.056 ms) : 0, 60056
Remote Config [baseline] (592.739 µs) : 0, 593
Remote Config [candidate] (584.264 µs) : 0, 584
Telemetry [baseline] (8.084 ms) : 0, 8084
Telemetry [candidate] (8.794 ms) : 0, 8794
Flare Poller [baseline] (5.845 ms) : 0, 5845
Flare Poller [candidate] (3.546 ms) : 0, 3546
section appsec
crashtracking [baseline] (1.197 ms) : 0, 1197
crashtracking [candidate] (1.185 ms) : 0, 1185
BytebuddyAgent [baseline] (655.626 ms) : 0, 655626
BytebuddyAgent [candidate] (656.242 ms) : 0, 656242
AgentMeter [baseline] (12.134 ms) : 0, 12134
AgentMeter [candidate] (12.08 ms) : 0, 12080
GlobalTracer [baseline] (258.025 ms) : 0, 258025
GlobalTracer [candidate] (257.728 ms) : 0, 257728
IAST [baseline] (24.346 ms) : 0, 24346
IAST [candidate] (24.138 ms) : 0, 24138
AppSec [baseline] (178.074 ms) : 0, 178074
AppSec [candidate] (177.14 ms) : 0, 177140
Debugger [baseline] (66.156 ms) : 0, 66156
Debugger [candidate] (66.06 ms) : 0, 66060
Remote Config [baseline] (633.937 µs) : 0, 634
Remote Config [candidate] (628.381 µs) : 0, 628
Telemetry [baseline] (8.38 ms) : 0, 8380
Telemetry [candidate] (8.335 ms) : 0, 8335
Flare Poller [baseline] (3.605 ms) : 0, 3605
Flare Poller [candidate] (3.64 ms) : 0, 3640
section iast
crashtracking [baseline] (1.192 ms) : 0, 1192
crashtracking [candidate] (1.227 ms) : 0, 1227
BytebuddyAgent [baseline] (796.766 ms) : 0, 796766
BytebuddyAgent [candidate] (797.451 ms) : 0, 797451
AgentMeter [baseline] (11.444 ms) : 0, 11444
AgentMeter [candidate] (11.399 ms) : 0, 11399
GlobalTracer [baseline] (246.634 ms) : 0, 246634
GlobalTracer [candidate] (246.92 ms) : 0, 246920
IAST [baseline] (25.312 ms) : 0, 25312
IAST [candidate] (25.298 ms) : 0, 25298
AppSec [baseline] (26.417 ms) : 0, 26417
AppSec [candidate] (26.383 ms) : 0, 26383
Debugger [baseline] (69.876 ms) : 0, 69876
Debugger [candidate] (70.147 ms) : 0, 70147
Remote Config [baseline] (526.764 µs) : 0, 527
Remote Config [candidate] (517.289 µs) : 0, 517
Telemetry [baseline] (9.725 ms) : 0, 9725
Telemetry [candidate] (9.1 ms) : 0, 9100
Flare Poller [baseline] (3.528 ms) : 0, 3528
Flare Poller [candidate] (3.295 ms) : 0, 3295
section profiling
crashtracking [baseline] (1.165 ms) : 0, 1165
crashtracking [candidate] (1.17 ms) : 0, 1170
BytebuddyAgent [baseline] (682.827 ms) : 0, 682827
BytebuddyAgent [candidate] (680.691 ms) : 0, 680691
AgentMeter [baseline] (9.009 ms) : 0, 9009
AgentMeter [candidate] (9.022 ms) : 0, 9022
GlobalTracer [baseline] (215.426 ms) : 0, 215426
GlobalTracer [candidate] (214.949 ms) : 0, 214949
AppSec [baseline] (32.057 ms) : 0, 32057
AppSec [candidate] (32.013 ms) : 0, 32013
Debugger [baseline] (64.922 ms) : 0, 64922
Debugger [candidate] (65.816 ms) : 0, 65816
Remote Config [baseline] (562.091 µs) : 0, 562
Remote Config [candidate] (556.071 µs) : 0, 556
Telemetry [baseline] (8.52 ms) : 0, 8520
Telemetry [candidate] (7.645 ms) : 0, 7645
Flare Poller [baseline] (3.434 ms) : 0, 3434
Flare Poller [candidate] (3.452 ms) : 0, 3452
ProfilingAgent [baseline] (93.505 ms) : 0, 93505
ProfilingAgent [candidate] (93.457 ms) : 0, 93457
Profiling [baseline] (94.069 ms) : 0, 94069
Profiling [candidate] (94.019 ms) : 0, 94019
LoadParameters
See matching parameters
SummaryFound 1 performance improvements and 3 performance regressions! Performance is the same for 17 metrics, 15 unstable metrics.
Request duration reports for petclinicgantt
title petclinic - request duration [CI 0.99] : candidate=1.60.0-SNAPSHOT~9911c514e7, baseline=1.61.0-SNAPSHOT~5580c61ac4
dateFormat X
axisFormat %s
section baseline
no_agent (17.07 ms) : 16902, 17238
. : milestone, 17070,
appsec (18.737 ms) : 18549, 18925
. : milestone, 18737,
code_origins (18.867 ms) : 18679, 19055
. : milestone, 18867,
iast (17.584 ms) : 17409, 17759
. : milestone, 17584,
profiling (18.853 ms) : 18662, 19043
. : milestone, 18853,
tracing (17.549 ms) : 17376, 17722
. : milestone, 17549,
section candidate
no_agent (18.71 ms) : 18521, 18899
. : milestone, 18710,
appsec (18.564 ms) : 18376, 18752
. : milestone, 18564,
code_origins (18.94 ms) : 18752, 19128
. : milestone, 18940,
iast (17.566 ms) : 17389, 17742
. : milestone, 17566,
profiling (19.516 ms) : 19323, 19710
. : milestone, 19516,
tracing (17.828 ms) : 17652, 18004
. : milestone, 17828,
Request duration reports for insecure-bankgantt
title insecure-bank - request duration [CI 0.99] : candidate=1.60.0-SNAPSHOT~9911c514e7, baseline=1.61.0-SNAPSHOT~5580c61ac4
dateFormat X
axisFormat %s
section baseline
no_agent (1.197 ms) : 1185, 1209
. : milestone, 1197,
iast (3.263 ms) : 3217, 3308
. : milestone, 3263,
iast_FULL (5.858 ms) : 5800, 5917
. : milestone, 5858,
iast_GLOBAL (3.519 ms) : 3467, 3570
. : milestone, 3519,
profiling (2.161 ms) : 2141, 2181
. : milestone, 2161,
tracing (1.786 ms) : 1771, 1802
. : milestone, 1786,
section candidate
no_agent (1.182 ms) : 1170, 1194
. : milestone, 1182,
iast (3.138 ms) : 3093, 3182
. : milestone, 3138,
iast_FULL (6.043 ms) : 5981, 6104
. : milestone, 6043,
iast_GLOBAL (3.605 ms) : 3548, 3662
. : milestone, 3605,
profiling (2.127 ms) : 2108, 2146
. : milestone, 2127,
tracing (1.782 ms) : 1767, 1796
. : milestone, 1782,
DacapoParameters
See matching parameters
SummaryFound 0 performance improvements and 0 performance regressions! Performance is the same for 12 metrics, 0 unstable metrics. Execution time for biojavagantt
title biojava - execution time [CI 0.99] : candidate=1.60.0-SNAPSHOT~9911c514e7, baseline=1.61.0-SNAPSHOT~5580c61ac4
dateFormat X
axisFormat %s
section baseline
no_agent (15.596 s) : 15596000, 15596000
. : milestone, 15596000,
appsec (14.737 s) : 14737000, 14737000
. : milestone, 14737000,
iast (18.122 s) : 18122000, 18122000
. : milestone, 18122000,
iast_GLOBAL (18.018 s) : 18018000, 18018000
. : milestone, 18018000,
profiling (15.39 s) : 15390000, 15390000
. : milestone, 15390000,
tracing (15.033 s) : 15033000, 15033000
. : milestone, 15033000,
section candidate
no_agent (15.684 s) : 15684000, 15684000
. : milestone, 15684000,
appsec (14.887 s) : 14887000, 14887000
. : milestone, 14887000,
iast (18.409 s) : 18409000, 18409000
. : milestone, 18409000,
iast_GLOBAL (17.712 s) : 17712000, 17712000
. : milestone, 17712000,
profiling (14.915 s) : 14915000, 14915000
. : milestone, 14915000,
tracing (14.834 s) : 14834000, 14834000
. : milestone, 14834000,
Execution time for tomcatgantt
title tomcat - execution time [CI 0.99] : candidate=1.60.0-SNAPSHOT~9911c514e7, baseline=1.61.0-SNAPSHOT~5580c61ac4
dateFormat X
axisFormat %s
section baseline
no_agent (1.474 ms) : 1463, 1486
. : milestone, 1474,
appsec (2.522 ms) : 2467, 2576
. : milestone, 2522,
iast (2.259 ms) : 2190, 2328
. : milestone, 2259,
iast_GLOBAL (2.298 ms) : 2229, 2367
. : milestone, 2298,
profiling (2.081 ms) : 2026, 2135
. : milestone, 2081,
tracing (2.064 ms) : 2010, 2117
. : milestone, 2064,
section candidate
no_agent (1.479 ms) : 1467, 1490
. : milestone, 1479,
appsec (2.565 ms) : 2508, 2622
. : milestone, 2565,
iast (2.253 ms) : 2184, 2322
. : milestone, 2253,
iast_GLOBAL (2.3 ms) : 2231, 2369
. : milestone, 2300,
profiling (2.077 ms) : 2023, 2132
. : milestone, 2077,
tracing (2.067 ms) : 2014, 2120
. : milestone, 2067,
|
5cd257e to
cbd6226
Compare
…wthTestOpenAiLlmInteractions::test_completion
…teractions::test_chat_completion_tool_call
…d with python openai instrumentation and system-tests
… with variables + chat_template, longest-first overlap handling) and support map-based LLM input serialization (messages + prompt) in LLMObs mapper. Also filter empty instruction messages to match system-test expectations.
…st and return [image] (not empty) when stripped input_image URLs are missing, aligning mixed-input chat_template output with expected behavior.
…output.messages from request params so existing error-span tests pass.
…ol_definitions tags
…JSON argument parsing and remove duplicate manual parsing logic from ResponseDecorator.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0c879ba692
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
...enai-java-3.0/src/main/java/datadog/trace/instrumentation/openai_java/ResponseDecorator.java
Outdated
Show resolved
Hide resolved
...enai-java-3.0/src/main/java/datadog/trace/instrumentation/openai_java/ResponseDecorator.java
Outdated
Show resolved
Hide resolved
Kyle-Verhoog
left a comment
There was a problem hiding this comment.
LLMObs Team Review
Nice work aligning the Java SDK payloads with the intake schema — this is a big step for system test compliance. A few items to address/clarify below (inline), plus some overall notes:
Test Coverage Notes
What's well-covered: LLMObsSpanMapperTest expansion is great — covers _dd map, nested meta.error, map-based input with prompt/chat_template, tool definitions, tool calls + tool results. The decorator tests verify the new tags (source, integration, error, ddtrace.version).
Gaps to consider:
- Error paths: No test exercises the error-path defaults (model_name and empty output set during
withResponseCreateParamswhen the HTTP call fails). A test where the response errors out and verifying the span still hasmodel_nameand placeholder output would be valuable. - Prompt tracking:
enrichInputWithPromptTracking(),extractChatTemplate(),extractPromptFromParams(), andnormalizePromptVariable()have no unit tests. Template variable replacement edge cases (overlapping values, empty variables, image/file fallbacks) would increase confidence. - Custom/MCP tool calls:
ToolCallExtractor.getToolCall(ResponseCustomToolCall)andgetToolCall(McpCall)are new with no unit tests. JsonValueUtils: New utility class with no dedicated tests for recursive JSON-to-Object conversion.
Questions
- The min version bump from
3.0.0to3.0.1— what API was missing in3.0.0? This affects which customer versions get instrumented. - For the
_ddmap — does the intake expectapm_trace_idto equaltrace_id? In other SDKs these can differ (APM trace ID vs LLMObs ID).
| # If you change the following comment, update the pattern in the update_system_test_reference.sh script to match. | ||
| uses: DataDog/system-tests/.github/workflows/system-tests.yml@main # system tests are pinned for releases only | ||
| uses: DataDog/system-tests/.github/workflows/system-tests.yml@ea458202a7673efbe365e498d64d74a815c0a137 # system tests are pinned for releases only | ||
| secrets: |
There was a problem hiding this comment.
System tests are pinned to commit ea458202 instead of main. The comment says "system tests are pinned for releases only." This should be reverted to main before merge to avoid blocking future system test updates. If it's intentional for CI during development, just make sure it goes back before merging.
There was a problem hiding this comment.
Yes I also think this is a bad manipulation (hence my review is only limited to this file :) )
There was a problem hiding this comment.
Yes, it was intentional and has just been reverted. The reason for this was to verify that the system tests pass with changes that are not yet part of the main branch: DataDog/system-tests#6364 Without that, none of the related tests would run at all.
| writable.writeString(errored ? "error" : "ok", null); | ||
| writable.writeUTF8(DD); | ||
| writable.startMap(3); | ||
| writable.writeUTF8(SPAN_ID); |
There was a problem hiding this comment.
The _dd map writes span_id, trace_id, and apm_trace_id. The Python SDK also includes t_id (64-bit trace ID) and s_id in the _dd map. Can you verify this is the correct subset for the Java path? If the intake expects additional fields, spans may be rejected or processed incorrectly.
There was a problem hiding this comment.
This is aligned with dd-trace-py https://github.com/DataDog/dd-trace-py/blob/876c5f1ce4d173815537798a6a7b0ac15b0a4ede/ddtrace/llmobs/_llmobs.py#L618-L622. I don't find any t_id or s_id there.
|
|
||
| boolean errored = span.getError() == 1; | ||
| writable.writeUTF8(STATUS); | ||
| writable.writeString(span.getError() == 0 ? "ok" : "error", null); |
There was a problem hiding this comment.
The top-level error: 0/1 integer field has been removed and replaced with status: "ok"/"error" + error details nested under meta.error. Can you confirm no downstream consumers (EvP remapper, indexer facets, etc.) read error from the top level? This is a payload shape change that could be breaking if anything depends on the old field.
There was a problem hiding this comment.
This change is dictated by the TestOpenAiLlmInteractions::test_chat_completion assertion. I assume that the system test assertions are correct. Have they been verified as being compliant with the requirements of downstream consumers?
| } | ||
| } | ||
| } catch (Throwable ignored) { | ||
| // fall back to raw JSON if typed extraction is unavailable or fails |
There was a problem hiding this comment.
nit: catch (Throwable) swallows OutOfMemoryError, StackOverflowError, etc. Consider narrowing to catch (Exception) for both fallback paths here.
| boolean hasToolCalls = null != toolCalls && !toolCalls.isEmpty(); | ||
| boolean hasToolResults = null != toolResults && !toolResults.isEmpty(); | ||
| boolean hasContent = message.getContent() != null; | ||
| int mapSize = 1; |
There was a problem hiding this comment.
Behavioral change: previously content was always written (even as null). Now it's skipped when message.getContent() == null (e.g., tool-call-only messages). This is likely correct and matches Python SDK behavior, but worth confirming the intake handles messages without a content key.
There was a problem hiding this comment.
This change is driven by TestOpenAiResponses::test_responses_create_tool_call. When the content is null, it is expected to be missing; otherwise, the assertion fails.
6dcdaf4 to
717a8f0
Compare
| apply from: "$rootDir/gradle/java.gradle" | ||
|
|
||
| def minVer = '3.0.0' | ||
| def minVer = '3.0.1' |
There was a problem hiding this comment.
ResponseTextConfig fun verbosity(): Optional<Verbosity> was added in 3.0.1 openai/openai-java@c1de354#diff-6b385fb153d457757ba112e6117593cb59da6af308cce0f9b6f26e3885befc6cR73
ResponseTextConfig fun verbosity(): Optional was added in 3.0.1 openai/openai-java@c1de354#diff-6b385fb153d457757ba112e6117593cb59da6af308cce0f9b6f26e3885befc6cR73
This is aligned with dd-trace-py https://github.com/DataDog/dd-trace-py/blob/876c5f1ce4d173815537798a6a7b0ac15b0a4ede/ddtrace/llmobs/_llmobs.py#L618-L622. |
…and placeholder output set by withResponseCreateParams.
…f enrichInputWithPromptTracking(), extractChatTemplate(), extractPromptFromParams(), and normalizePromptVariable()
… format. Test cover extractPromptFromParams and related methods
What Does This Do
Aligns OpenAI Java LLMObs span payloads with expected intake/system-test schema by:
_ml_obs_tag.integration_ml_obs_tag.source_ml_obs_tag.ddtrace.version_ml_obs_tag.error_ml_obs_tag.error_typemodel_name(and stable placeholder output where applicable) is set on error paths forchat/completions/embeddings/responses.
input.prompt,variables,chat_template)tool_definitions)stream,tool_choice,text.verbosity, etc.)_ddmap with span/trace idsmeta.errorinputserialization (messages+prompt)tool_definitionsintometa.Motivation
OpenAI/LLMObs system tests exposed schema and tag mismatches in Java payloads (especially response spans, tool metadata, error mapping, and prompt tracking structure). This change brings Java output in line with expected LLMObs intake contract and behavior.
Additional Notes
openai-java-3.0min version updated from3.0.0to3.0.1.ResponseTextConfig
fun verbosity(): Optional<Verbosity>was added in 3.0.1 openai/openai-java@c1de354#diff-6b385fb153d457757ba112e6117593cb59da6af308cce0f9b6f26e3885befc6cR73DataDog/dd-apm-test-agent#280
DataDog/system-tests#6364
Contributor Checklist
type:and (comp:orinst:) labels in addition to any other useful labelsclose,fix, or any linking keywords when referencing an issueUse
solvesinstead, and assign the PR milestone to the issueJira ticket: [PROJ-IDENT]
Note: Once your PR is ready to merge, add it to the merge queue by commenting
/merge./merge -ccancels the queue request./merge -f --reason "reason"skips all merge queue checks; please use this judiciously, as some checks do not run at the PR-level. For more information, see this doc.